/*
 * factor.h
 *
 *  Created on: Jun 3, 2011
 *      Author: tqlong
 */

#ifndef FACTOR_H_
#define FACTOR_H_

/** A factor (function) is a map from assignment to value */
template<typename NUMBER>
  class Factor : public std::map<Assignment, NUMBER, AssignmentCompare>
  {
  public:
    typedef std::map<Assignment, NUMBER, AssignmentCompare> __Base;
    typedef NUMBER number_type;
  public:
    Factor(const Argument& argument) :
      argument_(argument)
    {
    }

    Factor()
    {
    }

    Factor&
    operator=(const Factor& f)
    {
      argument_ = f.argument_;
      return *this;
    }

    Factor&
    addArgument(int varIdx)
    {
      argument_.addVariable(varIdx);
      return *this;
    }

    const Argument&
    argument() const
    {
      return argument_;
    }

    /** The first assignment with all zeros */
    Assignment
    firstAssignment() const
    {
      return Assignment(argument_.n_var());
    }

    /** Check if there is some next assignment, return false of no more assignment available */
    bool
    isValid(const Assignment& assgn) const
    {
      if (assgn.n_var() != argument_.n_var())
        {
          std::cerr << "Number of variables mismatch, at " << __FILE__ << ":"
              << __LINE__ << std::endl;
          exit(1);
        }
      return assgn.n_var() > 0 && assgn[0] >= 0;
    }

    /** Check if argument list is empty */
    bool
    isEmpty() const
    {
      return argument_.empty();
    }

    number_type
    value(const Universe& u) const
    {
      Assignment a = firstAssignment();
      for (int i = 0; i < argument_.n_var(); i++)
        {
          int varIdx = argument_.varIdx(i);
          a[i] = u.value(varIdx);
          if (a[i] < 0 || a[i] >= u.cardinality(varIdx))
            {
              std::cerr << "Value not in range [0, cardinality), at "
                  << __FILE__ << ":" << __LINE__ << std::endl;
              exit(1);
            }
        }
      return value(a);
    }

    number_type
    value(const Assignment& a) const
    {
      typename __Base::const_iterator it = this->find(a);
      return it == this->end() ? number_type(0.0) : it->second;
    }

    Factor
    reduce(const Universe& u) const
    {
      Assignment a = firstAssignment().fixed(argument_, u);
      Argument arg = argument_.fixed(u);

      //std::cout << "reduce arg=" << arg << std::endl;

      Factor f_reduced(arg);
      if (arg.n_var() == 0) return f_reduced;

      Assignment a_reduced = a.reducedAssignment();
      while (isValid(a))
        {
          number_type fval = this->value(a);
          f_reduced[a_reduced] = fval;

          //a.print(); std::cout << " --> "; a_reduced.print(); std::cout << " = " << fval << std::endl;
          a.nextAssignment(argument_, u);
          //this->nextFixedAssignment(a);
          a_reduced = a.reducedAssignment();
        }
      return f_reduced;
    }
  protected:
    Argument argument_;
  };

#endif /* FACTOR_H_ */
